package com.yami.shop.service.impl;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yami.shop.bean.app.dto.ProdCommDataDto;
import com.yami.shop.bean.app.dto.ProdCommDto;
import com.yami.shop.bean.app.param.ProdCommParam;
import com.yami.shop.bean.enums.EsOperationType;
import com.yami.shop.bean.event.EsProductUpdateEvent;
import com.yami.shop.bean.model.*;
import com.yami.shop.bean.vo.ShopDetailVO;
import com.yami.shop.common.enums.StatusEnum;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.util.Arith;
import com.yami.shop.common.util.IPHelper;
import com.yami.shop.common.util.RedisUtil;
import com.yami.shop.dao.OrderItemMapper;
import com.yami.shop.dao.ProdCommMapper;
import com.yami.shop.dao.ProductMapper;
import com.yami.shop.manager.impl.LangManager;
import com.yami.shop.service.ProdCommService;
import com.yami.shop.service.UserService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 商品评论
 *
 * @author xwc
 * @date 2019-04-19 10:43:57
 */
@Slf4j
@Service
@AllArgsConstructor
public class ProdCommServiceImpl extends ServiceImpl<ProdCommMapper, ProdComm> implements ProdCommService {

    private final ProdCommMapper prodCommMapper;

    private final RedissonClient redissonClient;

    private final OrderItemMapper orderItemMapper;

    private final ApplicationEventPublisher eventPublisher;

    private final LangManager langManager;

    private final UserService userService;

    private final ProductMapper productMapper;

    @Override
    public ProdCommDataDto getProdCommDataByProdId(Long prodId) {
        ProdCommDataDto prodCommDataDto = prodCommMapper.getProdCommDataByProdId(prodId);
        //计算出好评率
        if (prodCommDataDto.getPraiseNumber() == 0 || prodCommDataDto.getNumber() == 0) {
            prodCommDataDto.setPositiveRating(0.0);
        } else {
            prodCommDataDto.setPositiveRating(Arith.div(Arith.mul(prodCommDataDto.getPraiseNumber(), 100), prodCommDataDto.getNumber(), 2));
        }
        return prodCommDataDto;
    }

    @Override
    public IPage<ProdCommDto> getProdCommDtoPageByUserId(Page page, String userId) {
        return prodCommMapper.getProdCommDtoPageByUserId(page, userId);
    }

    @Override
    public IPage<ProdCommDto> getProdCommDtoPageByProdId(Page page, Long prodId, Integer evaluate) {

        IPage<ProdCommDto> prodCommDtos = prodCommMapper.getProdCommDtoPageByProdId(page, prodId, evaluate);
        List<String> userIdList = prodCommDtos.getRecords().stream().map(ProdCommDto::getUserId).collect(Collectors.toList());
        List<User> userList;
        if (CollUtil.isNotEmpty(userIdList)) {
            userList = userService.list(new LambdaQueryWrapper<User>()
                    .in(User::getUserId, userIdList));
        } else {
            userList = new ArrayList<>();
        }
        Map<String, List<User>> userMap = userList.stream().collect(Collectors.groupingBy(User::getUserId));
        prodCommDtos.getRecords().forEach(prodCommDto -> {
            // 匿名评价
            if (prodCommDto.getIsAnonymous() == 1) {
                prodCommDto.setNickName(null);
                prodCommDto.setPic(null);
            }
            List<User> users = userMap.get(prodCommDto.getUserId());
            if (Objects.isNull(users) || users.size() == 0){
                prodCommDto.setIsWriteOff(true);
            }
        });
        return prodCommDtos;
    }

    @Override
    public IPage<ProdComm> getProdCommPage(Page page, ProdComm prodComm, Long shopId) {
        IPage<ProdComm> prodCommPage = prodCommMapper.getProdCommPage(page, prodComm, shopId);
        if (CollUtil.isEmpty(prodCommPage.getRecords())) {
            return prodCommPage;
        }
        List<Long> ids = prodCommPage.getRecords().stream().filter(item -> Objects.nonNull(item.getProdId())).map(ProdComm::getProdId).collect(Collectors.toList());
        Map<Long, ProdLang> prodLangMap = langManager.getProdLangMap(ids);
        for (ProdComm item : prodCommPage.getRecords()) {
            ProdLang prodLang = prodLangMap.get(item.getProdId());
            if (Objects.nonNull(prodLang)) {
                item.setProdName(prodLang.getProdName());
            }
        }
        return prodCommPage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void comm(OrderItem orderItem, ProdCommParam prodCommParam) {
        long count = count(new LambdaQueryWrapper<ProdComm>().eq(ProdComm::getOrderItemId, orderItem.getOrderItemId()));
        // 已评论的商品，不需要再操作
        if (count > 0) {
            log.info("商品已评论， orderItemId: {}", orderItem.getOrderItemId());
            return;
        }
        if (StrUtil.length(prodCommParam.getContent()) > 500) {
            // 截取字符串
            prodCommParam.setContent(StrUtil.subWithLength(prodCommParam.getContent(), 0, 500));
        }
        // 插入评论
        ProdComm prodComm = new ProdComm();
        prodComm.setShopId(orderItem.getShopId());
        prodComm.setProdId(orderItem.getProdId());
        prodComm.setOrderItemId(orderItem.getOrderItemId());
        prodComm.setUserId(orderItem.getUserId());
        prodComm.setShopId(orderItem.getShopId());
        prodComm.setScore(prodCommParam.getScore());
        prodComm.setContent(prodCommParam.getContent());
        prodComm.setPics(StrUtil.isBlank(prodCommParam.getPics()) ? null : prodCommParam.getPics());
        prodComm.setIsAnonymous(prodCommParam.getIsAnonymous());
        prodComm.setRecTime(prodCommParam.getRecTime());
        // 平台不需要控评
        prodComm.setStatus(1);
        prodComm.setPostip(IPHelper.getIpAddr());
        // 好评
        if (Objects.equals(prodComm.getScore(), 5) || Objects.equals(prodComm.getScore(), 4)) {
            prodComm.setEvaluate(0);
        }
        // 中评
        else if (Objects.equals(prodComm.getScore(), 3)) {
            prodComm.setEvaluate(1);
        }
        // 差评
        else if (Objects.equals(prodComm.getScore(), 1) || Objects.equals(prodComm.getScore(), 2)) {
            prodComm.setEvaluate(2);
        }
        prodCommMapper.insert(prodComm);

        // 更新订单项评论状态
        orderItem.setCommSts(1);
        orderItemMapper.updateById(orderItem);
        // 判断商品是否已删除
        Product product = productMapper.selectById(prodComm.getProdId());
        if(ObjectUtil.isEmpty(product)){
            return;
        }
        if (Objects.equals(product.getStatus(), StatusEnum.DELETE.value())){
            return;
        }
        eventPublisher.publishEvent(new EsProductUpdateEvent(prodComm.getProdId(), null, EsOperationType.UPDATE_PROD_COMM));
    }

    @Override
    public void deleteByProdIds(Set<Long> prodIds) {
        if (CollectionUtil.isNotEmpty(prodIds)) {
            prodCommMapper.deleteByProdIds(prodIds);
        }
    }

    @Override
    public List<Long> getCommNumRankSpuIdsByShopIdAndTime(String key, Long shopId, Integer dayNum, Integer expireTime, Integer esRenovationSpuSort) {
        Date time = null;
        if(dayNum != 0) {
            time = DateUtil.offsetDay(new DateTime(), -dayNum);
        }

        List<Long> spuIds = RedisUtil.getListRange(key,0L,10L);
        if (CollectionUtil.isNotEmpty(spuIds)) {
            return spuIds;
        }

        // 加锁，防止缓存击穿
        RLock rLock = redissonClient.getLock("redisson_lock:sold_num_rank:" + key);
        try {
            int lockWait = 30;
            if (rLock.tryLock(lockWait, lockWait, TimeUnit.SECONDS)) {
                spuIds = RedisUtil.getListRange(key,0L,10L);
                if (CollectionUtil.isEmpty(spuIds)) {
                    spuIds = prodCommMapper.getCommNumRankSpuIdsByShopIdAndTime(shopId,time,esRenovationSpuSort);
                    return spuIds;
                }
            } else {
                throw new YamiShopBindException("网络繁忙，请稍后再试");
            }
        } catch (InterruptedException e) {
            log.error("InterruptedException:", e);
        } finally {
            try {
                if (rLock.isLocked()) {
                    rLock.unlock();
                }
            } catch (Exception e) {
                log.error("Exception:", e);
            }
        }
        return spuIds;
    }

    @Override
    public List<ShopDetailVO> computeShopScore(Date startTime, Long shopId) {
        return prodCommMapper.computeShopScore(startTime, shopId);
    }
}
